//
// Created by chaseaishang on 23-4-15.
//

#include "Camera.h"
#include "keyCodes.h"


//
// Default camera values
const float YAW         = -90.0f;
const float PITCH       =  0.0f;
const float SPEED       =  2.5f;
const float SENSITIVITY =  0.1f;//鼠标回报率
const float ZOOM        =  45.0f;

OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top, float zNear, float zFar)
    :m_ProjectionMatrix(glm::ortho(left, right, bottom, top, zNear, zFar)),m_ViewMatrix(glm::mat4(1.0f))
{
    this->init(glm::vec3(0.0f, 0.0f, 3.0f),glm::vec3(0.0f, 1.0f, 0.0f)
    ,YAW,PITCH);
    RecalculateViewMatrix();
    //m_ViewProjectionMatrix=m_ProjectionMatrix*m_ViewMatrix;
}
void OrthographicCamera::init(glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f),
                              glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW,
                              float pitch = PITCH)
{//俯仰角(Pitch)、偏航角(Yaw)

    m_Position = position;
    m_WorldUp = up;
    m_Yaw = yaw;
    m_Pitch = pitch;
    m_ZoomLevel=ZOOM;

}

void OrthographicCamera::SetProjection(float left, float right, float bottom, float top, float zNear, float zFar)
{
    m_ProjectionMatrix = glm::ortho(left, right, bottom, top, zNear, zFar);
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

void OrthographicCamera::RecalculateViewMatrix()
{

    glm::vec3 front;
    front.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
    front.y = sin(glm::radians(m_Pitch));
    front.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
    m_Front = glm::normalize(front);
    // also re-calculate the Right and Up vector
    m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));  // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
    m_Up    = glm::normalize(glm::cross(m_Right, m_Front));


    //glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position) ;


    m_ViewMatrix = glm::lookAt(m_Position, m_Position + m_Front, m_WorldUp);
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

void OrthographicCamera::SetDirection(Camera_Movement direction, float velocity)
{
    if (direction == FORWARD)
        m_Position += m_Front * velocity;
    if (direction == BACKWARD)
        m_Position -= m_Front * velocity;
    if (direction == LEFT)
        m_Position -= m_Right * velocity;
    if (direction == RIGHT)
        m_Position += m_Right * velocity;
    RecalculateViewMatrix();
}
//========================================================================================================================
PerspectiveCamera::PerspectiveCamera(float fov,float aspectRatio, float zNear, float zFar)
:m_ProjectionMatrix(glm::perspective(glm::radians(fov),aspectRatio,zNear,zFar)),
 m_ViewMatrix(glm::mat4(1.0f))
{
    this->init(glm::vec3(0.0f, 0.0f, 3.0f),glm::vec3(0.0f, 1.0f, 0.0f)
            ,YAW,PITCH,fov);
    RecalculateViewMatrix();
    //m_ViewProjectionMatrix=m_ProjectionMatrix*m_ViewMatrix;
}
void PerspectiveCamera::init(glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f),
                              glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW,
                              float pitch = PITCH,
                              float zoom=ZOOM
                                      )
{//俯仰角(Pitch)、偏航角(Yaw)

    m_Position = position;
    m_WorldUp = up;
    m_Yaw = yaw;
    m_Pitch = pitch;
    m_ZoomLevel=zoom;

}

void PerspectiveCamera::SetProjection(float fov,float aspectRatio, float zNear, float zFar)
{
    m_ProjectionMatrix = glm::perspective(glm::radians(fov),aspectRatio,zNear,zFar);
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

void PerspectiveCamera::RecalculateViewMatrix()
{

    glm::vec3 front;
    front.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
    front.y = sin(glm::radians(m_Pitch));
    front.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
    m_Front = glm::normalize(front);
    // also re-calculate the Right and Up vector
    m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));  // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
    m_Up    = glm::normalize(glm::cross(m_Right, m_Front));


    //glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position) ;


    m_ViewMatrix = glm::lookAt(m_Position, m_Position + m_Front, m_WorldUp);
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

void PerspectiveCamera::SetDirection(Camera_Movement direction, float velocity)
{
    if (direction == FORWARD)
        m_Position += m_Front * velocity;
    if (direction == BACKWARD)
        m_Position -= m_Front * velocity;
    if (direction == LEFT)
        m_Position -= m_Right * velocity;
    if (direction == RIGHT)
        m_Position += m_Right * velocity;
    RecalculateViewMatrix();
}


//初始化 camera

void OrthographicCameraController::OnEvent(Event &e)
{

}

void OrthographicCameraController::OnUpdate(float ts)
{
    float velocity=m_MovementSpeed;

    if(Input::IsKeyPressedImpl(CSAS_KEY_A))
    {
        this->m_Camera.SetDirection(LEFT,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_D))
    {
        this->m_Camera.SetDirection(RIGHT,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_W))
    {
        this->m_Camera.SetDirection(FORWARD,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_S))
    {
        this->m_Camera.SetDirection(BACKWARD,velocity);
    }

}

OrthographicCameraController::OrthographicCameraController(float left,float right,float bottom,float top,float znear,float zfar,
                                                           float aspectRatio, bool rotation )
: m_Camera(left, right ,bottom, top, znear, zfar)
{//glm::ortho(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f)
    m_MovementSpeed=SPEED;
    m_MouseSensitivity=SENSITIVITY;
}


void PerspectiveCameraController::OnEvent(Event &e)
{

    EventDispatcher dispatcher(e);
    dispatcher.Dispatch<KeyPressedEvent>(GLCORE_BIND_EVENT_FN(PerspectiveCameraController::OnKeyPressed));

    dispatcher.Dispatch<MouseButtonPressedEvent>(GLCORE_BIND_EVENT_FN(PerspectiveCameraController::OnMouseButtonPressed));
    dispatcher.Dispatch<MouseButtonReleasedEvent>(GLCORE_BIND_EVENT_FN(PerspectiveCameraController::OnMouseButtonReleased));
    //dispatcher.Dispatch<MouseMovedEvent>(GLCORE_BIND_EVENT_FN(PerspectiveCameraController::OnMouseMoved));
    dispatcher.Dispatch<MouseScrolledEvent>(GLCORE_BIND_EVENT_FN(PerspectiveCameraController::OnMouseScrolled));

   // MouseScrolledEvent
//MouseScrolledEvent

}

void PerspectiveCameraController::OnUpdate(float ts)
{
    float velocity=m_MovementSpeed;



    auto m_New_MousePosition=Input::GetMousePositionImpl();


    if(m_mouse.Is_RButonPress())
    {
        float x_last,y_last;
        m_mouse.Get_MousePos(x_last,y_last);
        float xoffset = m_New_MousePosition.first - x_last;
        float yoffset = y_last - m_New_MousePosition.second;

        float sensitivity = m_MouseSensitivity*ts*100;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        float yaw,pitch;
        this->m_Camera.GetEulerAngle(yaw,pitch);
        yaw   += xoffset;
        pitch += yoffset;

        if(pitch > 89.0f)
            pitch = 89.0f;
        if(pitch < -89.0f)
            pitch = -89.0f;
        this->m_Camera.SetEulerAngle(yaw,pitch);
    }
    m_mouse.Set_MousePos(m_New_MousePosition.first,m_New_MousePosition.second);
    if(Input::IsKeyPressedImpl(CSAS_KEY_A))
    {
        this->m_Camera.SetDirection(LEFT,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_D))
    {
        this->m_Camera.SetDirection(RIGHT,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_W))
    {
        this->m_Camera.SetDirection(FORWARD,velocity);
    }
    if(Input::IsKeyPressedImpl(CSAS_KEY_S))
    {
        this->m_Camera.SetDirection(BACKWARD,velocity);
    }

}
bool PerspectiveCameraController::OnKeyPressed(KeyPressedEvent &e)
{
    if(e.GetKeyCode()==CSAS_KEY_SPACE)
    {
        m_rotate_on=!m_rotate_on;
        if(m_rotate_on)
        {
            m_MousePosition=Input::GetMousePositionImpl();
        }
    }
    return false;
}
bool PerspectiveCameraController::OnMouseScrolled(MouseScrolledEvent& e)
{
    if(m_ZoomLevel >= 1.0f && m_ZoomLevel <= 45.0f)
        m_ZoomLevel -= e.GetYOffset();
    if(m_ZoomLevel <= 1.0f)
        m_ZoomLevel = 1.0f;
    if(m_ZoomLevel >= 45.0f)
        m_ZoomLevel = 45.0f;


    m_Camera.SetProjection(m_ZoomLevel,
                           m_AspectRatio ,
                           0.1, 100.0f);
    return false;
}
PerspectiveCameraController::PerspectiveCameraController
        (float fov,float aspectRatio=960/720.0,float zNear=-400,float zFar=400.0)
        : m_Camera(fov,aspectRatio, zNear, zFar)
{
    m_AspectRatio=aspectRatio;
    m_MovementSpeed=SPEED;
    m_MouseSensitivity=SENSITIVITY;
    m_ZoomLevel=fov;

}

bool PerspectiveCameraController::OnMouseButtonPressed(MouseButtonPressedEvent &e)
{
    if(e.GetMouseButton()==CSAS_MOUSE_BUTTON_LEFT)
    {
        this->m_mouse.SetLButtonState(Mouse::State::CSAS_PRESSED);
    }
    else if(e.GetMouseButton()==CSAS_MOUSE_BUTTON_RIGHT)
    {
        this->m_mouse.SetRButtonState(Mouse::State::CSAS_PRESSED);
    }
    return false;
}

bool PerspectiveCameraController::OnMouseButtonReleased(MouseButtonReleasedEvent &e)
{
    if(e.GetMouseButton()==CSAS_MOUSE_BUTTON_LEFT)
    {
        this->m_mouse.SetLButtonState(Mouse::State::CSAS_RELEASED);
    }
    else if(e.GetMouseButton()==CSAS_MOUSE_BUTTON_RIGHT)
    {
        this->m_mouse.SetRButtonState(Mouse::State::CSAS_RELEASED);
    }
    return false;

}
// unuseful
bool PerspectiveCameraController::OnMouseMoved(MouseMovedEvent &e)
{
    float x_new=e.GetX();
    float y_new=e.GetX();
    if(m_mouse.Is_LButonPress())// drag model
    {

        float  x_last,y_last;
        m_mouse.Get_MousePos(x_last,y_last);
        float xoffset = x_new- x_last;
        float yoffset = y_last - y_new;


        //float yoffset = 0;
        float sensitivity = m_MouseSensitivity;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        yoffset*=0.5f;
        float yaw,pitch;
        this->m_Camera.GetEulerAngle(yaw,pitch);
        yaw   += xoffset;
        pitch += yoffset;

        if(pitch > 89.0f)
            pitch = 89.0f;
        if(pitch < -89.0f)
            pitch = -89.0f;
        this->m_Camera.SetEulerAngle(yaw,pitch);


    }
    if(m_mouse.Is_RButonPress())// drag scene
    {
        float  x_last,y_last;
        m_mouse.Get_MousePos(x_last,y_last);
        float xoffset = x_new- x_last;
        float yoffset = y_last - y_new;
        //float yoffset = 0;
        float sensitivity = m_MouseSensitivity;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        yoffset*=0.5f;
        float yaw,pitch;
        this->m_Camera.GetEulerAngle(yaw,pitch);
        yaw   += xoffset;
        pitch += yoffset;

        if(pitch > 89.0f)
            pitch = 89.0f;
        if(pitch < -89.0f)
            pitch = -89.0f;
        this->m_Camera.SetEulerAngle(yaw,pitch);
//        float xoffset = (x_new - x_last);
//        float yoffset = (y_last - y_new);
//        float sensitivity = m_MouseSensitivity;
//        xoffset *= sensitivity;
//        yoffset *= sensitivity;
//        float yaw,pitch;
//        this->m_Camera.GetEulerAngle(yaw,pitch);
//        yaw   += xoffset;
//        pitch += yoffset;
//
//        if(pitch > 89.0f)
//            pitch = 89.0f;
//        if(pitch < -89.0f)
//            pitch = -89.0f;
//        this->m_Camera.SetEulerAngle(yaw,pitch);





    }
    m_mouse.Set_MousePos(x_new,y_new);
    return false;
}

float PerspectiveCameraController::getMMovementSpeed() const {
    return m_MovementSpeed;
}

void PerspectiveCameraController::setMMovementSpeed(float mMovementSpeed) {
    m_MovementSpeed = mMovementSpeed;
}


